package com.superb.gateway.filter;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSON;
import com.superb.common.core.enums.DeviceType;
import com.superb.common.core.enums.SuperbCode;
import com.superb.common.core.exception.SuperbException;
import com.superb.common.core.model.Result;
import com.superb.common.properties.SuperbCustomProperties;
import com.superb.common.utils.HeardersUtils;
import com.superb.common.utils.StringUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * sa-token过滤器
 * 过滤器需要配合redis一起做公共token
 * 否则token在gateway转发给子工程时将会失效
 * @Author: ajie
 * @CreateTime: 2024-05-14 15:55
 */
@Slf4j
@Configuration
public class SaAuthFilter {

    @Resource
    private SuperbCustomProperties customProperties;

    /**
     * sa-token过滤器配置
     * addInclude：添加属于sa-token拦截的的地址为所有地址/**
     * addExclude：添加白名单，白名单于nacos ignoreUrl配置中配置，白名单为数组
     * @return
     */
    @Bean
    public SaReactorFilter reactorFilter() {
        return new SaReactorFilter()
                .addInclude("/**") // 拦截地址
                .addExclude(customProperties.getIgnoreUrl().toArray(String[]::new))
                .setAuth(auth -> {
                    // 登录校验 -- 拦截所有路由
                    SaRouter.match("/**")
                            .check(this::checkHeaders)
                            .notMatch(customProperties.getIgnoreServiceUrl().toArray(String[]::new))
                            .check(this::checkLogin);
                }).setError(err -> {
                    // 设置错误返回格式为JSON
                    SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
                    SaHolder.getResponse().setStatus(406);
                    if (err instanceof SuperbException e) {
                        return JSON.toJSONString(Result.error(e.getCode(), e.getMessage()));
                    }
                    String message = err.getMessage();
                    if (message.contains("被顶下线")) {
                        SaHolder.getResponse().setStatus(200);
                        return JSON.toJSONString(Result.error(SuperbCode.AUTH_OFFLINE_ERROR, "当前账号已在其他地方登录！"));
                    }
                    if (message.contains("被踢下线")) {
                        SaHolder.getResponse().setStatus(200);
                        return JSON.toJSONString(Result.error(SuperbCode.AUTH_OFFLINE_ERROR, "当前账号已下线，请重新登录！"));
                    }
                    if (message.contains("token 无效")) {
                        message = "登录凭证无效！";
                    }
                    return JSON.toJSONString(Result.error(SuperbCode.AUTH_ERROR, message));
                })
                .setBeforeAuth(auth -> {
                    // ---------- 设置跨域响应头 ----------
                    SaHolder.getResponse()
                            // 允许指定域访问跨域资源
                            .setHeader("Access-Control-Allow-Origin", "*")
                            // 允许所有请求方式
                            .setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
                            // 有效时间
                            .setHeader("Access-Control-Max-Age", "3600")
                            // 允许的header参数
                            .setHeader("Access-Control-Allow-Headers", "*");
                    // 如果是预检请求，则立即返回到前端
                    SaRouter.match(SaHttpMethod.OPTIONS)
                            .free(r -> System.out.println("--------OPTIONS预检请求，不做处理"))
                            .back();
                });
    }

    private void checkHeaders() {
        // 网关将租户与客户端id获取，没有则不往下传递
        SaRequest request = SaHolder.getRequest();
        String requestPath = request.getRequestPath();
        String tenantId = request.getHeader(HeardersUtils.tenantId);
        String clientId = request.getHeader(HeardersUtils.clientId);
        if (StringUtils.isBlank(tenantId)) {
            log.error("sa-token过滤器校验---请求不存在tenantId---url: {}", requestPath);
            throw new SuperbException(SuperbCode.REQUEST_ERROR, "无租户凭证");
        }
        if (StringUtils.isBlank(clientId)) {
            log.error("sa-token过滤器校验---请求不存在clientId---url: {}", requestPath);
            throw new SuperbException(SuperbCode.REQUEST_ERROR, "无客户端凭证");
        } else {
            DeviceType of = DeviceType.of(clientId);
            clientId = of.name();
        }
    }

    /**
     * 登录校验
     */
    private void checkLogin() {
        SaRequest request = SaHolder.getRequest();
        String authentication = request.getHeader(HeardersUtils.token);
        if (StringUtils.isBlank(authentication)) {
            log.error("sa-token过滤器校验---请求不存在token");
            throw new SuperbException(SuperbCode.AUTH_ERROR, "无登录凭证");
        } else {
            StpUtil.checkLogin();
        }
    }

}
